import MBCore from './mb_core'
import MBUtils from './mb_utils'
import MBDataSender from './mb_data_sender'
import { MBSocketEvent } from './mb_constants';

class MBQoS4SendDaemon {

    private intervalId: any = 0;
    private CHECH_INTERVAL: any = 5000;
    private MESSAGES_JUST$NOW_TIME: any = 3 * 1000;
    private QOS_TRY_COUNT: any = 2;
    private sentMessages = new Map();
    private sendMessagesTimestamp = new Map();
    private _excuting = false;



    excute() {
        // 极端情况下本次循环内可能执行时间超过了时间间隔，此处是防止在前一次还没有运行完的情况下又重复执行，从而出现无法预知的错误
        if (!this._excuting) {
            // 丢包列表（数组单元中的是Protocal对象）
            var lostMessages = new Array();
            // 执行doRetryCheck()检查消息重传列表，完成后执行onRetryCheck()
            this.onRetryCheck(this.doRetryCheck(lostMessages));
        }
    };

    doRetryCheck(lostMessages: any) {
        this._excuting = true;
        try {

            // 开始处理中 ************************************************
            var arr = Array.from(this.sentMessages.keys());
            for (var i = 0; i < arr.length; i++) {
                var key = arr[i];

                var p = this.sentMessages.get(key);
                if (p && p.QoS) {
                    // 达到或超过了最大重试次数（判定丢包）
                    // if(p.getRetryCount() >= this.QOS_TRY_COUNT)
                    if (p.retryCount && p.retryCount >= this.QOS_TRY_COUNT) {

                        // 将这个包加入到丢包列表
                        lostMessages.push(p);

                        // 从列表中称除之
                        this.remove(p.fp);
                    }
                    // 没有达到重传上限则开始进行重传
                    else {
                        //### 2015103 Bug Fix: 解决了无线网络延较大时，刚刚发出的消息在其应答包还在途中时被错误地进行重传
                        var sendMessageTimestamp = this.sendMessagesTimestamp.get(key);
                        var delta = MBUtils.getCurrentUTCTimestamp() - (sendMessageTimestamp == null ? 0 : sendMessageTimestamp);
                        // 该消息包是“刚刚”发出的，本次不需要重传它哦
                        if (delta <= this.MESSAGES_JUST$NOW_TIME) {
                            if (MBCore.debugEnabled()) {
                                console.log("【QoS】指纹为" + key + "的包距\"刚刚\"发出才" + delta
                                    + "ms(<=" + this.MESSAGES_JUST$NOW_TIME + "ms将被认定是\"刚刚\"), 本次不需要重传哦.");
                            }
                        }
                        //### 2015103 Bug Fix END
                        else {
                            var code = MBDataSender.sendCommonData(p);
                            //已成功重传
                            if (code === 0) {
                                // 重传次数+1
                                if (!p.retryCount) {
                                    p.retryCount = 0;
                                }
                                p.retryCount += 1;

                                if (MBCore.debugEnabled()) {
                                    console.log("【QoS】指纹为" + p.fp + "的消息包已成功进行重传，此次之后重传次数已达" + p.retryCount + "(最多" + this.QOS_TRY_COUNT + "次).");
                                }
                            } else {
                                console.log("【QoS】指纹为" + p.fp + "的消息包重传失败，它的重传次数之前已累计为" + p.retryCount + "(最多" + this.QOS_TRY_COUNT + "次).");
                            }
                        }
                    }
                }
                // value值为null，从列表中去掉吧
                else {
                    this.remove(key);
                }
            }
        }
        catch (eee) {
            console.log("【QoS】消息发送质量保证线程运行时发生异常：", eee)
        }

        return lostMessages;
    };

    onRetryCheck(al: any) {
        if (al && al.length > 0) {
            // 通知观察者这些包丢包了（目标接收者没有收到）
            this.notifyMessageLost(al);
        }
        this._excuting = false;
    };

    /**
     * 将未送达信息反馈给消息监听者。
     *
     * @param lostMessages {Array<Protocal>} 已被判定为“消息未送达”的消息列表
     */
    notifyMessageLost(lostMessages: any) {
        MBCore.emit(MBSocketEvent.SOCKET_EVENT_MESSAGE_LOST, lostMessages);
    };

    startup(immediately: any) {
        this.stop();

        // 立即执行1次
        if (immediately) {
            this.excute();
        }

        let that = this;
        // 定时执行
        this.intervalId = setInterval(function () {
            that.excute();
        }, this.CHECH_INTERVAL);
    };

    stop() {
        if (this.intervalId && this.intervalId !== 0)
            clearInterval(this.intervalId);
    };

    /**
     * 该包是否已存在于队列中。
     *
     * @param fingerPrint {String} 消息包的特纹特征码（理论上是唯一的）
     * @return {boolean} true表示是，否则不是
     */
    exist(fingerPrint: any) {
        return this.sentMessages.get(fingerPrint) != null;
    };

    /**
     * 推入一个消息包的指纹特征码.
     * <br>注意：本方法只会将指纹码推入，而不是将整个Protocal对象放入列表中。
     *
     * @param p {Protocal} Protocal对象
     */
    put(p: any) {
        if (p) {
            if (p.fp) {
                if (p.QoS) {
                    // 如果列表中已经存则仅提示（用于debug）
                    if (this.sentMessages.get(p.fp) != null) {
                        console.log("【QoS】指纹为" + p.fp + "的消息已经放入了发送质量保证队列，该消息为何会重复？（生成的指纹码重复？还是重复put？）");
                    }

                    this.sentMessages.set(p.fp, p)
                    // 同时保存时间戳
                    this.sendMessagesTimestamp.set(p.fp, MBUtils.getCurrentUTCTimestamp());
                }
            }
        }
    };

    /**
     * 移除一个消息包.
     * <p>
     * 此操作是在步异线程中完成，目的是尽一切可能避免可能存在的阻塞本类中的守护线程.
     *
     * @param fingerPrint {String} 消息包的特纹特征码（理论上是唯一的）
     */
    remove(fingerPrint: any) {
        this.sendMessagesTimestamp.delete(fingerPrint);
        let removedObj = this.sentMessages.get(fingerPrint);
        this.sentMessages.delete(fingerPrint);
        console.log("【QoS】指纹为" + fingerPrint + "的消息已成功从发送质量保证队列中移除(可能是收到接收方的应答也可能是达到了重传的次数上限)，重试次数="
            + (removedObj != null ? (removedObj.retryCount) : "none呵呵."));
    };

    /**
     * 清空缓存队列。
     * <p>
     * 调用此方法可以防止在APP不退出的情况下退出登陆MobileIMSDK时没有清除队列缓存，导致此时换用另一账号时发生数据交叉。
     *
     * @since 3.2
     */
    clear() {
        this.sentMessages.clear();
        this.sendMessagesTimestamp.clear();
    };

    /**
     * 队列大小.
     *
     * @return {int}
     */
    size() {
        return this.sentMessages.size;
    };

    /**
     * 设置心跳间隔（默认5000ms）。
     *
     * @param interval {int} 心跳间隔（单位：毫秒）
     */
    setCheckInterval(interval: any) {
        this.CHECH_INTERVAL = interval;
    };

    /**
     * 设置最大重发次数（默认2次）。
     *
     * @param cnt {int} 最大重发次数
     */
    setQoSTryCount(cnt: any) {
        this.QOS_TRY_COUNT = cnt;
    };
}

export default new MBQoS4SendDaemon();